﻿/*
 *  Author: Louis A. Leskoviansky
 *  Company: Artemis Solutions Group (http://www.artemis-solutions.com)
 */


using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;
using System.Web;
using System.Web.Compilation;
using System.Web.UI;
using System.Web.UI.HtmlControls;

namespace AjaxControlFramework
{
    public static class AjaxControlUtils
    {
        //------// Properties \\--------------------------------------------\\
        public delegate void CreateChildControlsMethod(Control control);

        private static object CreateChildControlsLock = new object();
        private static CreateChildControlsMethod _createChildControls = null;
        public static CreateChildControlsMethod CreateChildControls
        {
            get
            {
                if (_createChildControls == null)
                {
                    lock (CreateChildControlsLock)
                    {
                        if (_createChildControls == null)
                        {
                            // Initialize and cache the dynamic method delegate to the ASP.NET internal method "SaveViewStateRecursive".
                            MethodInfo createChildControlsMethodInfo = TypeOfControl.GetMethod("CreateChildControls", BindingFlags.NonPublic | BindingFlags.Instance, null, Type.EmptyTypes, null);
                            DynamicMethod dynamicMethod = new DynamicMethod("CreateChildControls", typeof(void), new Type[] { TypeOfControl }, true);

                            ILGenerator ilGenerator = dynamicMethod.GetILGenerator();
                            ilGenerator.Emit(OpCodes.Ldarg_0);
                            ilGenerator.Emit(OpCodes.Callvirt, createChildControlsMethodInfo);
                            ilGenerator.Emit(OpCodes.Ret);

                            _createChildControls = (CreateChildControlsMethod)dynamicMethod.CreateDelegate(typeof(CreateChildControlsMethod));
                        }
                    }
                }

                return _createChildControls;
            }
        }



        public delegate string GetUniqueIDPrefixMethod(Control control);

        private static object GetUniqueIDPrefixLock = new object();
        private static GetUniqueIDPrefixMethod _getUniqueIDPrefix = null;
        public static GetUniqueIDPrefixMethod GetUniqueIDPrefix
        {
            get
            {
                if (_getUniqueIDPrefix == null)
                {
                    lock (GetUniqueIDPrefixLock)
                    {
                        if (_getUniqueIDPrefix == null)
                        {
                            // Initialize and cache the dynamic method delegate to the ASP.NET internal method "SaveViewStateRecursive".
                            MethodInfo getUniqueIDPrefixMethodInfo = TypeOfControl.GetMethod("GetUniqueIDPrefix", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.ExactBinding, null, Type.EmptyTypes, null);
                            DynamicMethod dynamicMethod = new DynamicMethod("GetUniqueIDPrefix", typeof(string), new Type[] { TypeOfControl }, true);

                            ILGenerator ilGenerator = dynamicMethod.GetILGenerator();
                            ilGenerator.Emit(OpCodes.Ldarg_0);
                            ilGenerator.Emit(OpCodes.Callvirt, getUniqueIDPrefixMethodInfo);
                            ilGenerator.Emit(OpCodes.Ret);

                            _getUniqueIDPrefix = (GetUniqueIDPrefixMethod)dynamicMethod.CreateDelegate(typeof(GetUniqueIDPrefixMethod));
                        }
                    }
                }

                return _getUniqueIDPrefix;
            }
        }



        public delegate char GetClientIDSeparatorProperty(Control control);

        private static object GetClientIDSeparatorLock = new object();
        private static GetClientIDSeparatorProperty _getClientIDSeparator = null;
        public static GetClientIDSeparatorProperty GetClientIDSeparator
        {
            get
            {
                if (_getClientIDSeparator == null)
                {
                    lock (GetClientIDSeparatorLock)
                    {
                        if (_getClientIDSeparator == null)
                        {
                            // Initialize and cache the dynamic method delegate to the ASP.NET protected property "Events".
                            MethodInfo getClientIDSeparatorMethodInfo = TypeOfControl.GetProperty("ClientIDSeparator", BindingFlags.Instance | BindingFlags.NonPublic).GetGetMethod(true);
                            DynamicMethod dynamicMethod = new DynamicMethod("GetClientIDSeparator", typeof(char), new Type[] { TypeOfControl }, true);

                            ILGenerator ilGenerator = dynamicMethod.GetILGenerator();
                            ilGenerator.Emit(OpCodes.Ldarg_0);
                            ilGenerator.Emit(OpCodes.Call, getClientIDSeparatorMethodInfo);
                            ilGenerator.Emit(OpCodes.Ret);

                            _getClientIDSeparator = (GetClientIDSeparatorProperty)dynamicMethod.CreateDelegate(typeof(GetClientIDSeparatorProperty));
                        }
                    }
                }

                return _getClientIDSeparator;
            }
        }



        public delegate char GetIdSeparatorProperty(Control control);

        private static object GetIdSeparatorLock = new object();
        private static GetIdSeparatorProperty _getIdSeparator = null;
        public static GetIdSeparatorProperty GetIdSeparator
        {
            get
            {
                if (_getIdSeparator == null)
                {
                    lock (GetIdSeparatorLock)
                    {
                        if (_getIdSeparator == null)
                        {
                            // Initialize and cache the dynamic method delegate to the ASP.NET protected property "Events".
                            MethodInfo getIdSeparatorMethodInfo = TypeOfControl.GetProperty("IdSeparator", BindingFlags.Instance | BindingFlags.NonPublic).GetGetMethod(true);
                            DynamicMethod dynamicMethod = new DynamicMethod("GetIdSeparator", typeof(char), new Type[] { TypeOfControl }, true);

                            ILGenerator ilGenerator = dynamicMethod.GetILGenerator();
                            ilGenerator.Emit(OpCodes.Ldarg_0);
                            ilGenerator.Emit(OpCodes.Call, getIdSeparatorMethodInfo);
                            ilGenerator.Emit(OpCodes.Ret);

                            _getIdSeparator = (GetIdSeparatorProperty)dynamicMethod.CreateDelegate(typeof(GetIdSeparatorProperty));
                        }
                    }
                }

                return _getIdSeparator;
            }
        }



        public delegate ClientIDMode GetEffectiveClientIDModeProperty(Control control);

        private static object GetEffectiveClientIDModeLock = new object();
        private static GetEffectiveClientIDModeProperty _getEffectiveClientIDMode = null;
        public static GetEffectiveClientIDModeProperty GetEffectiveClientIDMode
        {
            get
            {
                if (_getEffectiveClientIDMode == null)
                {
                    lock (GetEffectiveClientIDModeLock)
                    {
                        if (_getEffectiveClientIDMode == null)
                        {
                            // Initialize and cache the dynamic method delegate to the ASP.NET protected property "Events".
                            MethodInfo getEffectiveClientIDModeMethodInfo = TypeOfControl.GetProperty("EffectiveClientIDMode", BindingFlags.Instance | BindingFlags.NonPublic).GetGetMethod(true);
                            DynamicMethod dynamicMethod = new DynamicMethod("GetEffectiveClientIDMode", typeof(ClientIDMode), new Type[] { TypeOfControl }, true);

                            ILGenerator ilGenerator = dynamicMethod.GetILGenerator();
                            ilGenerator.Emit(OpCodes.Ldarg_0);
                            ilGenerator.Emit(OpCodes.Callvirt, getEffectiveClientIDModeMethodInfo);
                            ilGenerator.Emit(OpCodes.Ret);

                            _getEffectiveClientIDMode = (GetEffectiveClientIDModeProperty)dynamicMethod.CreateDelegate(typeof(GetEffectiveClientIDModeProperty));
                        }
                    }
                }

                return _getEffectiveClientIDMode;
            }
        }
        //------\\ Properties //--------------------------------------------//



        //------// Fields \\------------------------------------------------\\
        private static Type TypeOfPage = typeof(System.Web.UI.Page);
        private static Type TypeOfControl = typeof(System.Web.UI.Control);
        //------\\ Fields //------------------------------------------------//



        //------// Methods \\-----------------------------------------------\\
        public static List<HtmlControl> FindChildControlsByTagName(Control parent, string tagName)
        {
            List<HtmlControl> controls = new List<HtmlControl>();
            FindChildControlsByTagName(parent, tagName.ToLower(), ref controls);

            return controls;
        }


        private static void FindChildControlsByTagName(Control parent, string tagName, ref List<HtmlControl> controls)
        {
            if (parent.Controls.Count == 0) { return; }

            foreach (HtmlControl childControl in parent.Controls.OfType<HtmlControl>())
            {
                if (childControl.TagName.ToLower() == tagName)
                {
                    controls.Add(childControl);
                }

                FindChildControlsByTagName(childControl, tagName, ref controls);
            }
        }


        public static List<Control> FindNamedChildControls(Control parent, bool onlyFindTopLevelControls)
        {
            List<Control> controls = new List<Control>();

            if (onlyFindTopLevelControls)
            {
                FindNamedChildControls(parent, ref controls);
            }
            else
            {
                FindNamedChildControlsRecursive(parent, ref controls);
            }

            return controls;
        }


        private static void FindNamedChildControlsRecursive(Control parent, ref List<Control> controls)
        {
            if (parent.Controls.Count == 0) { return; }

            foreach (Control childControl in parent.Controls)
            {
                if (childControl.ID != null)
                {
                    controls.Add(childControl);
                }

                FindNamedChildControls(childControl, ref controls);
            }
        }


        private static void FindNamedChildControls(Control parent, ref List<Control> controls)
        {
            if (parent.Controls.Count == 0) { return; }

            foreach (Control childControl in parent.Controls)
            {
                if (childControl.ID != null)
                {
                    controls.Add(childControl);
                }
            }
        }


        public static IList<Control> FindControls(Control parent, bool findOnlyTopLevelControls, params string[] controlIDs)
        {
            List<Control> controls = new List<Control>();
            if (controlIDs == null) { return controls; }

            if (findOnlyTopLevelControls)
            {
                FindControls(parent, ref controls, controlIDs);
            }
            else
            {
                FindControlsRecursive(parent, ref controls, controlIDs);
            }

            return controls;
        }


        private static void FindControlsRecursive(Control parent, ref List<Control> controls, params string[] controlIDs)
        {
            if (parent.Controls.Count == 0) { return; }

            foreach (Control childControl in parent.Controls)
            {
                if (controlIDs.Contains(childControl.ID))
                {
                    controls.Add(childControl);
                }

                FindControlsRecursive(childControl, ref controls, controlIDs);
            }
        }


        private static void FindControls(Control parent, ref List<Control> controls, params string[] controlIDs)
        {
            if (parent.Controls.Count == 0) { return; }

            foreach (Control childControl in parent.Controls)
            {
                if (controlIDs.Contains(childControl.ID))
                {
                    controls.Add(childControl);
                }
            }
        }


        public static Control FindChildControlByUniqueID(Control parent, string controlID)
        {
            if (controlID == null) { return null; }
            Control control = null;

            FindChildControlByUniqueIDRecursive(parent, ref control, controlID);

            return control;
        }


        private static void FindChildControlByUniqueIDRecursive(Control parent, ref Control control, string controlID)
        {
            if (parent.Controls.Count == 0) { return; }

            foreach (Control childControl in parent.Controls)
            {
                if (controlID == childControl.UniqueID)
                {
                    control = childControl;
                    break;
                }

                FindChildControlByUniqueIDRecursive(childControl, ref control, controlID);
            }
        }


        public static Control FindTopMostParent(Control child)
        {
            if (child == null) { return null; }

            Control topMostControl = child;
            FindTopMostParent(child, ref topMostControl);

            return topMostControl;
        }


        private static Control FindTopMostParent(Control child, ref Control topMostParent)
        {
            Control parent = child.Parent;

            if (parent == null)
            {
                return child;
            }
            else
            {
                topMostParent = parent;
                return FindTopMostParent(parent, ref topMostParent);
            }
        }


        public static Control FindTopMostParent(Page child)
        {
            if (child == null) { return null; }

            Control topMostControl = child;
            FindTopMostParent(child, ref topMostControl);

            return topMostControl;
        }


        private static Control FindTopMostParent(Page child, ref Control topMostParent)
        {
            MasterPage parent = child.Master;

            if (parent == null)
            {
                return child;
            }
            else
            {
                topMostParent = parent;
                return FindTopMostParent(parent, ref topMostParent);
            }
        }


        private static Control FindTopMostParent(MasterPage child, ref Control topMostParent)
        {
            MasterPage parent = child.Master;

            if (parent == null)
            {
                return child;
            }
            else
            {
                topMostParent = parent;
                return FindTopMostParent(parent, ref topMostParent);
            }
        }


        public static T FindParentControlByType<T>(Control child) where T : class
        {
            T parentControl = default(T);
            FindParentControlByType<T>(child, ref parentControl);

            return parentControl;
        }


        private static void FindParentControlByType<T>(Control child, ref T parentControl) where T : class
        {
            Control parent = child.Parent;

            if (parent == null)
            {
                parentControl = default(T);
            }
            else
            {
                if (parent is T)
                {
                    parentControl = parent as T;
                }
                else
                {
                    FindParentControlByType<T>(parent, ref parentControl);
                }
            }
        }


        public static T FindParentControlByType<T>(Control child, Control topMostParent) where T : class
        {
            if (topMostParent == null)
            {
                return FindParentControlByType<T>(child);
            }

            T parentControl = default(T);
            FindParentControlByType<T>(child, topMostParent, ref parentControl);

            return parentControl;
        }


        private static void FindParentControlByType<T>(Control child, Control topMostParent, ref T parentControl) where T : class
        {
            Control parent = child.Parent;

            if (parent == null)
            {
                parentControl = default(T);
            }
            else
            {
                if (Object.ReferenceEquals(parent, topMostParent))
                {
                    return;
                }
                else if (parent is T)
                {
                    parentControl = parent as T;
                }
                else
                {
                    FindParentControlByType<T>(parent, topMostParent, ref parentControl);
                }
            }
        }


        public static List<T> FindChildControlsByType<T>(Control parent) where T : class
        {
            List<T> controls = new List<T>();
            FindChildControlsByTypeRecursive<T>(parent, ref controls);

            return controls;
        }


        private static void FindChildControlsByTypeRecursive<T>(Control parent, ref List<T> controls) where T : class
        {
            controls.AddRange(parent.Controls.OfType<T>());

            foreach (Control childControl in parent.Controls)
            {
                FindChildControlsByTypeRecursive<T>(childControl, ref controls);
            }
        }


        public static T FindChildControlByType<T>(Control parent) where T : class
        {
            T control = default(T);
            FindChildControlByTypeRecursive<T>(parent, ref control);

            return control;
        }


        private static void FindChildControlByTypeRecursive<T>(Control parent, ref T control) where T : class
        {
            T desiredChild = parent.Controls.OfType<T>().FirstOrDefault();

            if (desiredChild == null)
            {
                foreach (Control childControl in parent.Controls)
                {
                    FindChildControlByTypeRecursive<T>(childControl, ref control);
                }
            }
            else
            {
                control = desiredChild;
            }
        }


        public static Type ResolveControlType(string fullyQualifiedTypeName)
        {
            return ResolveControlType(fullyQualifiedTypeName, null);
        }


        public static Type ResolveControlType(string fullyQualifiedTypeName, Assembly assemblyTip)
        {
            if (assemblyTip != null)
            {
                string unqualifiedTypeName = DetermineUnqualifiedTypeName(fullyQualifiedTypeName);

                return assemblyTip.GetType(unqualifiedTypeName, false, true);
            }
            else
            {
                Type controlType = BuildManager.GetType(fullyQualifiedTypeName, false, true);

                if (controlType == null)
                {
                    string unqualifiedTypeName = DetermineUnqualifiedTypeName(fullyQualifiedTypeName);

                    Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();

                    foreach (Assembly assembly in assemblies)
                    {
                        controlType = assembly.GetType(unqualifiedTypeName, false, true);

                        if (controlType != null) { break; }
                    }
                }

                return controlType;
            }
        }


        public static Type ResolveBaseControlType(string fullyQualifiedTypeName)
        {
            return ResolveBaseControlType(ResolveControlType(fullyQualifiedTypeName));
        }


        // This method takes into account the Types generated during dynamic compilation of Template Controls.
        public static Type ResolveBaseControlType(Type controlType)
        {
            if (controlType != null && typeof(TemplateControl).IsAssignableFrom(controlType))
            {
                if (controlType.Name.EndsWith("_jsx"))
                {
                    controlType = controlType.BaseType.BaseType;
                }
                else if (controlType.Name.EndsWith("_ascx") || controlType.Name.EndsWith("_aspx"))
                {
                    controlType = controlType.BaseType;
                }
            }

            return controlType;
        }


        public static string DetermineUnqualifiedTypeName(string fullyQualifiedTypeName)
        {
            int indexOfFirstComma = fullyQualifiedTypeName.IndexOf(',');

            if (indexOfFirstComma > -1)
            {
                return fullyQualifiedTypeName.Substring(0, indexOfFirstComma).Trim();
            }

            return fullyQualifiedTypeName.Trim();
        }


        public static string GetIDFromUniqueID(Control control, string uniqueID)
        {
            string prefix = GetUniqueIDPrefix(control);
            if (prefix.Length == 0) { return uniqueID; }
            
            return uniqueID.Replace(prefix, String.Empty).Trim();
        }


        // The Control parameter is necessary to accurately query ASP.NET for the ClientID and UniqueID separator characters.
        public static string GetUniqueIDFromClientID(Control control, string clientID)
        {
            return clientID.Replace(GetClientIDSeparator(control), GetIdSeparator(control)); 
        }


        // The Control parameter is necessary to accurately query ASP.NET for the ClientID and UniqueID separator characters.
        public static string GetClientIDFromUniqueID(Control control, string uniqueID)
        {
            return uniqueID.Replace(GetIdSeparator(control), GetClientIDSeparator(control));
        }
        //------\\ Methods //-----------------------------------------------//
    }
}
